package com.sasken.pdb.controllers
{
	import com.sasken.pdb.models.data.DBObject;
	import com.sasken.pdb.models.settings.ListUtil;
	import com.sasken.pdb.views.windows.ConfirmationWindow;
	import com.sasken.pdb.views.windows.UnsavedChangesWindow;
	import com.sasken.pdb.views.windows.Window;
	
	import flash.events.Event;
	import flash.events.MouseEvent;
	
	import mx.collections.IList;
	import mx.core.Application;
	import mx.core.UIComponent;
	import mx.events.CloseEvent;
	import mx.managers.PopUpManager;
	import mx.validators.Validator;
	 
	/**
	 *  This class is a base class for all window controllers. It 
	 *  contains basic behavior for saving, deleting, showing confirmation windows
	 *  and window title. It must be extended to be utilized.
	 */  		
	public class WindowController extends BaseController
	{
		private var _titlePrefix:String;
	   	private var _titleSuffix:String;
	   	
	   	private var _view:Window;
	   	
	   	[Bindable]
        protected var hasUnsavedChanges:Boolean;
        private var _dataModel:DBObject;
        // the class of dataModel
        private var _dataClass:Class;
	   	
	   	private var _useDefaultCloseListener:Boolean;
	   	
	   	private var _validators:Array;
	   	
	   	/**
		 *  Constructor, initializes this object.
		 * 
		 * @param controlledView The view associated with this controller.
		*/
		public function WindowController(controlledView:UIComponent)
		{
			_useDefaultCloseListener = true;
			super(controlledView);
			
			super.view = controlledView;
			_view = controlledView as Window;
			
			validators = new Array();
			
			hasUnsavedChanges = false;
        	_view.addEventListener(CloseEvent.CLOSE, closeWindowListener);
		}
		
		/**
		 * Sets the window title's prefix, which determines the type of the window. For 
		 * example, it can be the name of a project or full name of an employee
		 * associated with the window. Prefix and suffix are separated
		 * so that the suffix could be used in confirmation windows directly.
		 * @param suffix The suffix title of the window.
		 * <p></p>
		*/
	   	protected function set titleSuffix(suffix:String):void
	   	{
	   		_titleSuffix = suffix; 
	   		updateTitle();   		
	   	}
	   	
	   	protected function set validators(val:Array):void
	   	{
	   		_validators = val;
	   	}
	   	
	   	protected function get validators():Array
	   	{
	   		return _validators;
	   	}
	   	
	   	/**
		 * Sets the window title's prefix, which determines the type of the window. For 
		 * example, "Equipment Profile" or "Extend Project". Prefix and suffix are separated
		 * so that the suffix could be used in confirmation windows directly.
		 * @param prefix The prefix title of the window.
		 * <p></p>
		*/
	   	protected function set titlePrefix(prefix:String):void
	   	{
	   		_titlePrefix = prefix; 
	   		updateTitle();
	   	}
	   	
	   	/**
		 * 
		 * 
		 * 
		 * <p></p>
		*/
	    protected function updateTitle():void
	   	{
	   		_view.title = _titlePrefix + ": " + _titleSuffix;
	   	}
	    
	    /**
		 * Sets the data model used by this window.
		 * 
		 * @param mod The data model used by this window. 
		 * <p></p>
		*/				   	
		public function set dataModel(mod:DBObject):void
		{
			_dataModel = mod;
		}
		
		/**
		 * Returns the data model used by this window.
		 * @return the data model used by this window.
		*/
		public function get dataModel():DBObject
		{
			return _dataModel;
		}
        
        /**
        *  Tests if two values are equal. It is able to 
        *  parse data types used in windows such as strings and dates.
        * 
        */ 
        protected function areInequal(modelValue:Object, viewValue:Object):Boolean
        {
        	// Only viewValue is used for checking because it's guaranteed to have some
        	// value, even if it's empty.
        	if (viewValue is String)
        	{
        		if (viewValue != "" && modelValue != viewValue) 
				{
					return true;
				}
        	}
        	else if (viewValue is Date)
        	{
        		var d1:Date = modelValue as Date;
        		var d2:Date = viewValue as Date;
        		
        		var viewValueTime:int = (viewValue is Date) ? (viewValue as Date).getTime() : -1;
        		var modelValueTime:int = (modelValue is Date) ? (modelValue as Date).getTime() : -1;
        		
        		if ((viewValueTime != modelValueTime))
        		{
        			return true;
        		}
        	}
        	else if (viewValue is Boolean)
        	{
        		if (modelValue != viewValue)
        		{
        			return true;
        		}
        	}
        	else if (viewValue is IList)
        	{
        		if (!ListUtil.listsEqual(modelValue as IList, viewValue as IList))
        		{
        			return true;
        		}
        	}
        	
        	return false;
        }
        
        /**
		 * Listener for close window events. Declared protected so that
		 * additional methods of closing than just the standard one could be utilized
		 * by inherited classes in the same way.
		 * <p>By default, the function validates the model, checks for saving changes and 
		 * displays a dialog if necessary.</p>
		 * @param e The event sent to this listener.
		*/
        protected function closeWindowListener(e:Event):void
        {
        	//trace("closeWindowListener");
        	
        	if (!useDefaultCloseListener) return;

        	testUnsavedChanges();

        	if (hasUnsavedChanges)
        	{
				var unWin:UnsavedChangesWindow = new UnsavedChangesWindow();
				PopUpManager.addPopUp(unWin, Application.application as Application, true);
				unWin.visible = false;
				unWin.title = "Save changes: " + _titleSuffix;
				unWin.controller.confirmationText = "You have unsaved changes. Are you sure you want to close the window?";
				unWin.addEventListener(CloseEvent.CLOSE, unsavedChangesListener);
				PopUpManager.centerPopUp(unWin);
				unWin.visible = true;
				
        	}
        	else
        	{
        		closeCleanup();
        	}
        
        }
        
        /**
		 * 
		 * 
		 * @param 
		 * <p></p>
		*/
        private function unsavedChangesListener(e:CloseEvent):void
        {
        	if (e.detail == UnsavedChangesWindowController.RESULT_DONT_SAVE_CHANGES)
        	{
        		closeCleanup();
        	}
        	else if (e.detail == UnsavedChangesWindowController.RESULT_CANCEL)
        	{
        		// nothing
        	}
        	else if (e.detail == UnsavedChangesWindowController.RESULT_SAVE_CHANGES)
        	{
        		if (saveModel())
        		{
        			closeCleanup();
        		}
        	}
        	
        }
        
        /**
        *  Sets the class of the data model so it can be used when saving and deleting models.
        *  Must be called by the extending controller.
        *  @param cls The class of dataModel.
        */ 
        protected function set dataModelClass(cls:Class):void
        {
        	_dataClass = cls;
        }
        
        /**
        *  Returns the class of data provider used by this window.
        * 
        */ 
        protected function get dataModelClass():Class
        {
        	return _dataClass;
        }
        
        /**
		 * This function must be called when the Save button is pressed in windows that
		 * support saving its models. This function sends the save command to the server
		 * through DataModelRemoting, which will save the model according to its type.
		 * 
		*/
        protected function saveModel():Boolean
        {
        	hasUnsavedChanges = false;
        	
        	_dataModel.save();
        	
        	closeCleanup();
        	
        	return true;
        }
        
        /**
		 * Do cleanup things related to closing a window. Must be called 
		 * even from overriden implementations.
		 * 
		 * <p></p>
		*/
        protected function closeCleanup():void
		{
			PopUpManager.removePopUp(_view);	
		}
        
        /**
		 * 
		 * 
		 * @param 
		 * <p></p>
		*/
        protected function closeButtonClickListener(e:MouseEvent):void
        {
        	closeWindowListener(null);
        	//_view.dispatchEvent(new CloseEvent(CloseEvent.CLOSE, false, true));
        }
        
        protected function validateModel():Boolean
        {
        	var result:Array = Validator.validateAll(_validators);
			if (result.length == 0)
			{
				return true;	
			}
			else
			{
				return false;
			}
        }
        
        /**
        *  This function can be used by a subclass by overriding it to validate a 
        *  model that it uses for its data. By default, it does not do anything, 
        *  but it can be used to check for validity as well as unsaved changes. 
        * 
        */ 
        protected function testUnsavedChanges():void
        {
        
        }
        
        /**
        *  
        */ 
        protected function displayDeletionConfirmation():void
        {
        	var confirmationWindow:ConfirmationWindow = new ConfirmationWindow();
        	confirmationWindow.visible = false;
			PopUpManager.addPopUp(confirmationWindow, Application.application as Application, true);
			
			confirmationWindow.title = "Delete: " + _titleSuffix;
			confirmationWindow.controller.confirmationText = "Are you sure you want to permanently delete '" + _titleSuffix + "'?";
			confirmationWindow.controller.actionName = "Delete";
			PopUpManager.centerPopUp(confirmationWindow);
			confirmationWindow.visible = true;
			confirmationWindow.addEventListener(CloseEvent.CLOSE, deleteConfirmationListener);

        }
        
        private function deleteConfirmationListener(e:CloseEvent):void
        {
        	if (e.detail == ConfirmationWindowController.RESULT_DO_ACTION)
        	{
        		deleteModel();
        	}
        }
        
        /**
		 * This function must be called when the Delete button is pressed in windows that
		 * support deleting its entire contents. This function must be overridden by the 
		 * extended controller class. 
		 * <p>When the function is called, a confirmation dialog is displayed.</p>
		*/
		protected function deleteModel():void
		{
			_dataModel.deleteFromDB();
			closeCleanup();
		}
		
		protected function set useDefaultCloseListener(value:Boolean):void
		{
			_useDefaultCloseListener = value;
		}
		
		protected function get useDefaultCloseListener():Boolean
		{
			return _useDefaultCloseListener;
		}
		
	
	}
}